package nautilus.service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.UUID;

import nautilus.lab.MainActivity;
import nautilus.utility.ByteQueue;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

public class DataService extends Service{

	private static String TAG = "DataService";
	private static final byte[] CLOSECMD = {(byte)0xFE, (byte)0xFE, (byte)0xFF, (byte)0xFE, (byte)0xFF};
	private static final UUID MY_UUID = UUID.fromString("04c6093b-0000-1000-8000-00805f9b34fb");
	
	private TCPConnection tcpConnection = null;
	private BlueConnection blueConnection = null;
	private Thread queueHandlingThread = null;
	private ByteQueue byteQueue;
	private LocalBroadcastManager localBroadcastManager;
	private DataReceiver dataReceiver = null;
	
	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public void onCreate(){
		localBroadcastManager = LocalBroadcastManager.getInstance(getApplicationContext());
		dataReceiver = new DataReceiver();
		IntentFilter intentFilter = new IntentFilter("NAUTILUS_DATA_COMMUNICATION");
		localBroadcastManager.registerReceiver(dataReceiver, intentFilter);
	}
	
	@Override
	public int onStartCommand(Intent intent, int flag, int startId){
		byteQueue = new ByteQueue(1024);
		int requestedService = intent.getIntExtra("requested_service", MainActivity.BLUECONNECT);
		if(requestedService == MainActivity.BLUECONNECT){
			String deviceName = intent.getStringExtra("device_name");
			startBlue(deviceName);
		}else{
			String serverHost = intent.getStringExtra("server_host");
			int port = intent.getIntExtra("port", 9909);
			startTCP(serverHost, port);
		}
		
		// Start queue handler 
		queueHandlingThread = new Thread(new Runnable(){
			public void run(){
				byte[] data;
				Intent intentBC;
				try{
					while(true){
						synchronized(byteQueue){
							byteQueue.wait();
							data = byteQueue.removeAll();
							if(data != null && data.length > 0)	{
								intentBC = new Intent("SOCKET_RECEIVED");
								intentBC.putExtra("data", data);
								//DataService.this.sendBroadcast(intentBC);
								localBroadcastManager.sendBroadcast(intentBC);
								//Log.i(TAG, "Broadcast data");
							}
							byteQueue.notify();
						}
					}
				}catch(InterruptedException e){
					Log.e(TAG, e.getMessage());
				}
			}});
		queueHandlingThread.start();
		
		return START_NOT_STICKY;
	}
	
	@Override
	public void onDestroy(){
		try{
			localBroadcastManager.unregisterReceiver(dataReceiver);
			
			Thread stoppingThread = new Thread(new Runnable(){
				public void run(){
					if(tcpConnection != null)
						tcpConnection.closeSocket();
					
					if(blueConnection != null)
						blueConnection.closeSocket();
				}
			});
			stoppingThread.start();
			stoppingThread.join();
		}catch(InterruptedException ex){
			Log.e(TAG, ex.getMessage());
		}
		//localBroadcastManager.unregisterReceiver(arg0);
		super.onDestroy();
	}
	
	private void startTCP(String serverHost, int port){
		if(tcpConnection == null){			
			tcpConnection = new TCPConnection(serverHost, port, byteQueue);
			tcpConnection.start();
		}
	}
	
	private void startBlue(String deviceName){
		blueConnection = new BlueConnection(deviceName, byteQueue);
		blueConnection.start();
	}
	
	class TCPConnection extends Thread {
		private Socket socket;
		private volatile boolean isRunning = false;
		private byte[] buffer = new byte[1024];
		private ByteQueue byteQueue;
		private OutputStream outStream;
		private String serverHost;
		private int port;
		
		public TCPConnection(String host, int p, ByteQueue bq){
			byteQueue = bq;
			serverHost = host;
			port = p;
		}
		
		public void sendData(byte[] data){
			try{
				outStream.write(data);
				outStream.flush();
			}catch(IOException e1){
				Log.e(TAG, e1.getMessage());
			}
		}
		
		public void closeSocket(){
			try{
				sendData(CLOSECMD);
				isRunning = false;
				socket.close();
			}catch(IOException e1){
				Log.e(TAG, e1.getMessage());
			}
		}
		
		@Override
		public void run(){
			InputStream inStream = null;
			try{
				socket = new Socket(serverHost, port);
				isRunning = true;
				outStream = socket.getOutputStream();
				inStream = socket.getInputStream();
				
				int i, byteRead = 0;
				while(isRunning){
					if( (byteRead = inStream.read(buffer, 0, 1024)) > 0 ){
						byteQueue.add(buffer, byteRead);
						for(i=0; byteRead>4 && i<byteRead-4; i++){
							if(buffer[i]==CLOSECMD[0] && buffer[i+1]==CLOSECMD[1] && buffer[i+2]==CLOSECMD[2] &&
									buffer[i+3]==CLOSECMD[3] &&	buffer[i+4]==CLOSECMD[4]){
								isRunning = false;
								break;
							}
						}
					}
				}	
				socket.close();
			}catch(IOException e1){
				//Actually we don't need to do anything here
				Log.e(TAG, e1.getMessage());
			}catch(InterruptedException e2){
				Log.e(TAG, e2.getMessage());
			}
		}
	}

	class BlueConnection extends Thread{
		private BluetoothDevice device;
		private BluetoothSocket blueSocket;
		private InputStream inStream;
		private OutputStream outStream = null;
		private boolean isRunning = true;
		private BluetoothAdapter blueAdapter;
		private ByteQueue byteQueue;
		
		public BlueConnection(String deviceName, ByteQueue bq){
			blueAdapter = BluetoothAdapter.getDefaultAdapter();
			device = blueAdapter.getRemoteDevice(deviceName);
			byteQueue = bq;
		}
		
		@Override
		public void run(){
			
			try{
				//Step 1. Open Bluetooth Socket
				blueSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
				/* Cancel discovery because it will slow down system performance */
				blueAdapter.cancelDiscovery();
				
				try{
					blueSocket.connect();
					Log.i(TAG, "Bluetooth connect OK");
				}catch(IOException e){
					//do something to try re-connect
					Log.e(TAG, e.getMessage());
				}
				
				inStream = blueSocket.getInputStream();
				outStream = blueSocket.getOutputStream();
				
				byte[] buffer = new byte[1024];
				int byteRead = 0;
				while(isRunning){
					if( inStream.available() > 0)
						if( (byteRead=inStream.read(buffer))>0 ){
							byteQueue.add(buffer, byteRead);
					}
				}
			}catch(IOException e1){
				Log.e(TAG, e1.getMessage());
			}catch(InterruptedException e2){
				Log.e(TAG, e2.getMessage());
			}
		}
		
		public void closeSocket(){
			try{
				sendData(CLOSECMD);
				isRunning = false;
				blueSocket.close();
			}catch(IOException e1){
				Log.e(TAG, e1.getMessage());
			}
		}
		
		public void sendData(byte[] data){
			try{
				if(outStream == null)
					return;
				outStream.write(data);
				outStream.flush();
			}catch(IOException e1){
				Log.e(TAG, e1.getMessage());
			}
		}
	}
	
	class DataReceiver extends BroadcastReceiver{
		@Override
		public void onReceive(Context context, Intent intent) {			
			if (intent.getAction().equals("SOCKET_RECEIVED")) {
				
			}else if(intent.getAction().equals("NAUTILUS_DATA_COMMUNICATION")){
				byte cmd = intent.getExtras().getByte("CTRL_DATA");
				if(blueConnection != null){
					blueConnection.sendData(new byte[]{cmd});
					Log.i(TAG, "Just send data to blue server.");
				}
			}
		}
		
	}
}
